import { _decorator, Component, error, instantiate, Node, NodePool, Prefab, Tween, tween, UITransform, v3, Vec3 } from 'cc';
import { FontItem } from './FontItem';
const { ccclass, property } = _decorator;

@ccclass('FontBubblePool')
export class FontBubblePool extends Component {
    /****************************************************************************************************************
     *Readonly 常量
     ****************************************************************************************************************/
    /****************************************************************************************************************
     *Property 定义变量
     ****************************************************************************************************************/
    /**
     * 文本对象池Map
     */
    private poolMap: Map<number, NodePool> = new Map();

    /**
     * 等待显示的数组
     */
    protected queue: ItemValue[] = [];


    /**
     * 预制的Tween效果
     */
    private tweenMap: Map<number, Tween<unknown>> = new Map();


    /**
     * 文本显示倒计时
     */
    private downTime: number = 0;
    /***************************************************************
     *PropertyUI 引用的控件
     ***************************************************************/
    @property({ type: [Prefab], tooltip: `弹出文本效果预制体(预制体根节点应当包含FontItem组件)` })
    private fontItemPrefabs: Prefab[] = []!;

    @property({ tooltip: `文本显示间隔(秒),实际生效最小值为每帧间隔` })
    private interval: number = 0.01;

    @property({ tooltip: `文本显示时,每次显示几个文本` })
    private fontWaveSize: number = 1;

    //0:按照顺序显示全部文本 但是如果产生的文本一直
    // @property({ group: { name: '显示效果', id: '1', displayOrder: 1 }, type: CCInteger, tooltip: `文本显示模式 0:显示全部文本` })
    // private mode: 0 | 1 = 0;
    @property({ tooltip: `最大队列数量,超过队列数量时开始从队列中丢弃数据,设置为0时不限制队列大小(不建议)` })
    private maxQueueSize: number = 1000;


    @property({ tooltip: `默认单个对象池大小` })
    private poolSize: number = 20;


    /****************************************************************************************************************
     * Lifecycle 生命周期相关方法
     ****************************************************************************************************************/
    protected onLoad(): void {
        this.initTweenStyle();
    }

    protected update(dt: number): void {
        this.downTime -= dt;
        if (this.downTime <= 0) {
            this.popItem();
            this.downTime = this.interval;
        }
    }
    /****************************************************************************************************************
     * Public 公有方法
     ****************************************************************************************************************/
    /**
     * 显示文本效果
     * @param string 文本内容 传入FontItem.show(string: string)的参数
     * @param fontStyle 
     * @param tweenType 
     */
    public show(string: string, fontStyle: number = 0, parent: Node = undefined, pos: Vec3 = undefined, tweenType: number = 0) {
        let value: ItemValue = { string: string, fontStyle: fontStyle, tweenType: tweenType, parent: parent, pos: pos };

        /**
         * 队列最大数量设置不为0,且队列数目超过设置的最大值
         */
        if (this.maxQueueSize && (this.queue.length >= this.maxQueueSize)) {
            this.dropQueueItem();
        }

        this.queue.push(value);
    }
    /****************************************************************************************************************
     * private 私有方法
     ****************************************************************************************************************/
    private popItem() {

        if (this.queue.length <= 0) {
            return;
        }

        let values: ItemValue[] = [];

        for (let i = 0; i < this.fontWaveSize; i++) {
            if (this.queue.length <= 0) {
                break;
            }

            let value = this.queue.shift();
            if (!value) {
                continue;
            }

            values.push(value);
        }

        for (let i = 0; i < values.length; i++) {
            let value = values[i];
            this.showItem(value);
        }
    }

    /**
     * 显示Item效果
     * @param value 
     */
    private showItem(value: ItemValue): Promise<void> {
        let styleTween = this.tweenMap.get(value.tweenType);
        if (!styleTween) {
            error(`不存在缓动样式风格:${value.tweenType}`);
            return Promise.reject(`不存在缓动样式风格:${value.tweenType}`);
        }

        let itemNode = this.getItem(value.fontStyle);
        if (!itemNode) {
            error(`获取节点失败`);
            return Promise.reject(`获取节点失败`);
        }
        let item = itemNode.getComponent(FontItem);
        if (!item) {
            error(`找不到FontItem组件`);
            return Promise.reject(`找不到FontItem组件`);
        }

        item.value = value.string

        let pos = value.pos;
        if (!pos) {
            pos = this.itemPosition();
        }

        let parent = value.parent;
        if (!parent) {
            parent = this.node;
        }

        itemNode.setPosition(pos);
        parent.addChild(itemNode);

        let pool = this.poolMap.get(value.fontStyle);

        let tw: Tween<Node> = styleTween.clone(itemNode)

        tw.call(() => {
            if (pool) {
                pool.put(itemNode);
            }
        }).start()
    }

    /**
     * 根据样式获取要显示Item节点
     * @param style 
     * @returns 
     */
    private getItem(style: number): Node {
        let pool = this.poolMap.get(style);

        if (!pool) {
            pool = this.initPool(style);
        }

        let node: Node = pool.get();
        if (!node) {
            node = this.createItem(style);
        }

        return node;
    }

    /**
     * 初始化对应样式对象池
     * @param style 样式
     * @returns 
     */
    private initPool(style: number): NodePool {
        if (style >= this.fontItemPrefabs.length) {
            error(`文本样式${style}预制体不存在`)
            return;
        }

        let pool = new NodePool("FontItem");
        let prefab = this.fontItemPrefabs[style];

        if (!prefab) {
            error(`文本样式${style}预制体不存在或者未初始化`)
            return;
        }

        for (let i = 0; i < this.poolSize; i++) {
            let node = this.createItem(style);
            pool.put(node);
        }
        this.poolMap.set(style, pool);
        return pool;
    }

    /**
     * 根据样式创建Item
     * @param style 
     * @returns 
     */
    private createItem(style: number): Node {
        if (style >= this.fontItemPrefabs.length) {
            error(`文本样式${style}预制体不存在`)
            return;
        }

        let prefab = this.fontItemPrefabs[style];
        let node = instantiate(prefab);
        return node;
    }
    /**********************************************************************************************
     * Protected
     **********************************************************************************************/
    /**
     * 预制Tween效果
     */
    protected initTweenStyle() {
        this.tweenMap.clear();
        let t0 = tween().by(0.5, { position: v3(0, 100, 0) }, { easing: "elasticOut" }).by(0.5, { position: v3(0, 200, 0) });
        this.tweenMap.set(0, t0);
    }

    /**
     * item添加到节点时的位置
     */
    protected itemPosition(): Vec3 {
        let position: Vec3 = new Vec3();
        if (!this.node) {
            return position;
        }
        let ui = this.node.getComponent(UITransform);
        if (!ui) {
            ui = this.node.addComponent(UITransform);
        }

        let randX = Math.random() * ui.width - ui.width / 2;
        let randY = Math.random() * ui.height - ui.height / 2;
        position.x = randX;
        position.y = randY;
        return position;
    }


    /**
     * 队列中数据满时候,丢弃队列中部分数据
     * @param value 丢弃数量
     */
    protected dropQueueItem(value: number = 1) {
        for (let i = 0; i < value; i++) {
            this.queue.shift();
        }
    }
}
/**
 * 待显示的文本数据
 */
interface ItemValue {
    /**
     * 要显示的文本内容
     */
    string: string,
    /**
     * 要显示的文本样式
     */
    fontStyle: number,
    /**
     * 文字出现要隐藏的Tween效果
     */
    tweenType: number,

    /**
     * 父节点
     */
    parent: Node,

    /**
     * 文本位置
     */
    pos: Vec3
}


